#include "ofxImage.h"

ofxImage::ofxImage()
{
	pixelcount = 0;
	ocvImg = nullptr;
	ocvImgDirty = false;
}


//////////////////////////////////////////////////////////////////////////
//						OPERATOR OVERLOADS
//////////////////////////////////////////////////////////////////////////
unsigned char& ofxImage::operator[](int i){
	return pixels[i];
}


//////////////////////////////////////////////////////////////////////////
//						OFIMAGE OVERLOADS
//////////////////////////////////////////////////////////////////////////

void ofxImage::draw( float x, float y )
{
	ofImage::draw(x, y);
}

void ofxImage::draw()
{
	ofImage::draw(0,0);
}

void ofxImage::draw( float x, float y, float width, float heigth )
{
	ofImage::draw(x,y,width,heigth);
}

bool ofxImage::loadImage(string filename){
	if(ofImage::loadImage(filename)){
		sourcename = filename;
		init();
		return true;
	}
	return false;
}

void ofxImage::setImageType(int type){
	if(type != ofImage::type){
		ofImage::setImageType(type);
		init();
	}
}


//////////////////////////////////////////////////////////////////////////
//						LOAD AND CHANGES
//////////////////////////////////////////////////////////////////////////
bool ofxImage::loadImage(string filename, string basedir, int type) 
{
	bool success;
	filename = basedir+"/"+filename;
	success = ofImage::loadImage(filename);

	if(success){
		sourcename = filename;
		if((type == OF_IMAGE_COLOR || type == OF_IMAGE_COLOR_ALPHA || type == OF_IMAGE_GRAYSCALE) 
			&& (type != this->type)){
				this->setImageType(type);
		}
	}

	init();

	console::log("Load "+filename, success);
	return success;
}


void ofxImage::ocvSetup2(){
	int channels;

	switch(type){
	case OF_IMAGE_GRAYSCALE:
		channels = 1;
		break;
	case OF_IMAGE_COLOR:
		channels = 3;
		break;
	case OF_IMAGE_COLOR_ALPHA:
		channels = 4;
		break;
	}

	ocvImage = cvCreateImageHeader(cvSize(width, height), IPL_DEPTH_8U, channels);
	ocvImage->imageData = (char*)pixels;
}


void ofxImage::ocvSetup()
{
	if(type == OF_IMAGE_GRAYSCALE || type == OF_IMAGE_COLOR){

		bool alloc = false;

		//Create image?
		if(ocvImg == nullptr){
			alloc = true;
		} else if(!ocvFormatCompatible()){
			delete ocvImage;
			alloc = true;
		}

		if(alloc){

			if(type == OF_IMAGE_GRAYSCALE){
				ocvImg = new ofxCvGrayscaleImage();
			} else{
				ocvImg = new ofxCvColorImage();
			}
			ocvImg->allocate(width, height);
			ocvImg->setFromPixels(pixels, width, height);
			ocvImgOutdated= false;
		} 			
	} 
	
	else if(type == OF_IMAGE_COLOR_ALPHA){
		std::cout << "Image '"<< sourcename << "' does not support openCv filters." << std::endl;
	}
	else{
		ofLog(OF_LOG_ERROR, "Invalid image type at ofxImage::ocvSetup");
	}
}


bool ofxImage::ocvFormatCompatible()
{
	IplImage* cvImg = ocvImg->getCvImage();

	if(type == OF_IMAGE_GRAYSCALE && cvImg->nChannels != 1){
		return false;
	} else if(type == OF_IMAGE_COLOR && cvImg->nChannels != 3){
		return false;
	} else if(ocvImg->width != width){
		return false;
	} else if(ocvImg->height != height){
		return false;
	}

	return true;
}


void ofxImage::init()
{
	pixelcount = width * height;
	pixels = ofImage::getPixels();
	original.setFromPixels(pixels, width, height, type);
	ocvSetup();
}

void ofxImage::restore()
{
	ofImage::setFromPixels(original.getPixels(), original.width, original.height, original.type);
	pixels = ofImage::getPixels();
	pixelcount = width * height;
	ocvImgOutdated = true;
}

void ofxImage::commitPixelOps()
{
	ofImage::setFromPixels(pixels, width, height, type);
	ocvImgOutdated = true;
}

void ofxImage::commitFilterOps()
{
	if(ocvImgDirty && ocvImg != nullptr){
		ofImage::setFromPixels(ocvImg->getPixels(), width, height, type);
		pixels = ofImage::getPixels();
		ocvImgDirty = false;
	}
}

//////////////////////////////////////////////////////////////////////////
//							OPENCV FILTERING
//////////////////////////////////////////////////////////////////////////
void ofxImage::open(int itterations)
{
	if(type == OF_IMAGE_COLOR_ALPHA){
		std::cout << "Filtering not supported on 4 channel images" << std::endl;
	} else{
		prepareFilterOp();
		filter.morphology(ocvImg, &filterKernel, CV_MOP_OPEN, itterations);
		ocvImgDirty = true;
	}
}

void ofxImage::close(int itterations)
{
	if(type == OF_IMAGE_COLOR_ALPHA){
		std::cout << "Filtering not supported on 4 channel images" << std::endl;
	} else{
		prepareFilterOp();
		filter.morphology(ocvImg, &filterKernel, CV_MOP_CLOSE, itterations);
		ocvImgDirty = true;
	}
}

void ofxImage::blackhat(int itterations)
{
	if(type == OF_IMAGE_COLOR_ALPHA){
		std::cout << "Filtering not supported on 4 channel images" << std::endl;
	} else{
		prepareFilterOp();
		filter.morphology(ocvImg, &filterKernel, CV_MOP_BLACKHAT, itterations);
		ocvImgDirty = true;
	}
}

void ofxImage::tophat(int itterations)
{
	if(type == OF_IMAGE_COLOR_ALPHA){
		std::cout << "Filtering not supported on 4 channel images" << std::endl;
	} else{
		prepareFilterOp();
		filter.morphology(ocvImg, &filterKernel, CV_MOP_TOPHAT, itterations);
		ocvImgDirty = true;
	}
}

void ofxImage::erode( int itterations )
{
	if(type == OF_IMAGE_COLOR_ALPHA){
		std::cout << "Filtering not supported on 4 channel images" << std::endl;
	} else{
		prepareFilterOp();
		filter.erode(ocvImg, &filterKernel, itterations);
		ocvImgDirty = true;
	}
}

void ofxImage::dilate( int itterations )
{
	if(type == OF_IMAGE_COLOR_ALPHA){
		std::cout << "Filtering not supported on 4 channel images" << std::endl;
	} else{
		prepareFilterOp();
		filter.dilate(ocvImg, &filterKernel, itterations);
		ocvImgDirty = true;
	}
}

void ofxImage::blurgaussian(int value)
{
	if(type == OF_IMAGE_COLOR_ALPHA){
		std::cout << "Filtering not supported on 4 channel images" << std::endl;
	} else{
		prepareFilterOp();
		ocvImg->blurGaussian(value);
		ocvImgDirty = true;
	}
}

void ofxImage::blur(int value)
{
	if(type == OF_IMAGE_COLOR_ALPHA){
		std::cout << "Filtering not supported on 4 channel images" << std::endl;
	} else{
		prepareFilterOp();
		ocvImg->blur(value);
		ocvImgDirty = true;
	}
}

void ofxImage::threshold( int value, bool invert)
{
	if(type == OF_IMAGE_COLOR_ALPHA){
		std::cout << "Filtering not supported on 4 channel images" << std::endl;
	} else{
		if(type != OF_IMAGE_GRAYSCALE){
			setImageType(OF_IMAGE_GRAYSCALE);
		}
		prepareFilterOp();
		filter.threshold(ocvImg, value, invert);
		ocvImgDirty = true;
	}
}

void ofxImage::setFilterSize( int width, int height)
{
	if(width > 0 && height > 0){
		filterKernel.setSize(width, height);
		filterKernel.setAnchor(width/2, height/2);
	} else{
		std::cout << "Illegal filter size" << endl;
	}
}

void ofxImage::changeFilterSize( int widthDelta, int heigthDelta )
{
	filterKernel.changeSize(widthDelta, heigthDelta);
}

filterProperties ofxImage::getFilterProperties()
{
	return filterKernel.getProperties();
}

void ofxImage::prepareFilterOp()
{
	if(ocvImgOutdated){
		ocvImg->setFromPixels(pixels, width, height);
		ocvImgOutdated= false;
	}
}

void ofxImage::setRoi( int x, int y, int w, int h )
{
	if(ocvImg != nullptr){
		ocvImg->setROI(x, y, w, h);
	}
}

UCHAR* ofxImage::getPixels()
{
	return pixels;
}



